﻿// Copyright 2010 Maxime Serrano <lastchronicler@gmail.com>
// Copyright 2009, 2010 Matvei Stefarov <me@matvei.org>
using System;
using System.Net;
using System.IO;
using System.Collections.Generic;

namespace MCSharp {
    static class InfoCommands {
        public const string RuleFile = "rules.txt";
        // Register help commands
        internal static void Init() {
            CommandList.RegisterCommand(cdInfo);
            CommandList.RegisterCommand(cdClassInfo);

            CommandList.RegisterCommand(cdGetVersion);
            CommandList.RegisterCommand(cdRules);
            CommandList.RegisterCommand(cdHelp);

            CommandList.RegisterCommand(cdWhere);
            CommandList.RegisterCommand(cdWhois);

            CommandList.RegisterCommand(cdPlayers);
            CommandList.RegisterCommand(cdClasses);
        }

        static CommandDescriptor cdPlayers = new CommandDescriptor {
            name = "players",
            consoleSafe = true,
            usage = "/players",
            help = "Lists all players on the server. ",
            handler = Players
        };

        internal static void Players(Player player, Command cmd) {
            Player[] players = Server.playerList;
            if (players.Length > 0) {
                string playerListString = "There " + (players.Length == 1 ? "is 1 player" : "are " + players.Length + " players" ) + " on the server: ";
                bool first = true;
                foreach (Player p in players) {
                    if (!first) playerListString += ", ";
                    playerListString += p.info.playerClass.color + p.nick;
                    first = false;
                }
                player.Message(playerListString);
            } else {
                player.Message("There appear to be no players on the server.");
            }
        }

        static CommandDescriptor cdGetVersion = new CommandDescriptor {
            name = "version",
            consoleSafe = true,
            help = "Shows server software name and version.",
            handler = GetVersion
        };

        internal static void GetVersion(Player player, Command cmd) {
            player.Message("MCSharp custom server version UNKNOWN");
        }

        static CommandDescriptor cdWhere = new CommandDescriptor {
            name = "where",
            aliases = new string[] { "compass" },
            consoleSafe = true,
            usage = "/where [PlayerName]",
            help = "Shows information about the location and orientation of a player. " +
                   "If no name is given, shows player's own info.",
            handler = Where
        };

        static string compass = "N . . . nw. . . W . . . sw. . . S . . . se. . . E . . . ne. . . " +
                                "N . . . nw. . . W . . . sw. . . S . . . se. . . E . . . ne. . . ";

        internal static void Where(Player player, Command cmd) {
            int offset;
            string name = cmd.Next();

            Player target = player;

            if (name != null) {
                target = Server.FindPlayer(name);
                if (target != null) {
                    player.Message("Coordinates of player \"" + target.nick + "\":");
                } else {
                    player.NoPlayerMessage(name);
                    return;
                }
            } else if (player.session == null) {
                player.Message("When called from console, " + Color.Help + "/where" + Color.Sys + " requires a player name.");
                return;
            }

            offset = (int)(((360f - target.pos.rotation) - 90f) / 360f * 64f) + 32;

            player.Message(Color.Silver + String.Format("( {0}, {1}, {2} ) - {3}[{4}{5}{6}{3}{7}]",
                            target.pos.x.ToString("N2"),
                            target.pos.y.ToString("N2"),
                            target.pos.z.ToString("N2"),
                            Color.White,
                            compass.Substring(offset - 12, 11),
                            Color.Red,
                            compass.Substring(offset - 1, 3),
                            compass.Substring(offset + 2, 11)));
        }

        static CommandDescriptor cdHelp = new CommandDescriptor {
            name = "help",
            consoleSafe = true,
            usage = "/help [CommandName]",
            help = "...",
            handler = Help
        };

        const string HelpPrefix = "&S    ";
        internal static void Help(Player player, Command cmd) {
            string commandName = cmd.Next();

            if (commandName == "commands") {
                if (cmd.Next() != null) {
                    player.Message("    ", "List of all available commands:&N" + CommandList.GetCommandList(player, true));
                } else {
                    player.Message("    ", "List of all commands:&N" + CommandList.GetCommandList(player, false));
                }

            } else if (commandName != null) {
                CommandDescriptor descriptor = CommandList.GetDescriptor(commandName);
                if (descriptor == null) {
                    player.Message("Unknown command: \"" + cmd.name + "\"");
                    return;
                }

                string helpString = Color.Help + descriptor.usage + "&N";

                if (descriptor.aliases != null) {
                    string aliases = "Aliases: &H";
                    bool first = true;
                    foreach (string alias in descriptor.aliases) {
                        aliases += (first ? "" : "&S, &H") + alias;
                        first = false;
                    }
                    helpString += aliases + "&N";
                }

                if (descriptor.helpHandler != null) {
                    helpString += descriptor.helpHandler(player);
                } else {
                    helpString += descriptor.help;
                }
                player.Message(HelpPrefix, helpString);

            } else {
                player.Message("To see a list of all commands, write " + Color.Help + "/help commands");
                player.Message("To see detailed help for a command, write " + Color.Help + "/help CommandName");
                if (player.session != null) {
                    player.Message("To find out about your permissions, write " + Color.Help + "/class " + player.info.playerClass.name);
                }
                player.Message("To send private messages, write " + Color.Help + "@PlayerName Message");
                player.Message("To message all players of a class, write " + Color.Help + "@@Class Message");
            }
        }

        static CommandDescriptor cdWhois = new CommandDescriptor {
            name = "whois",
            consoleSafe = true,
            usage = "/whois PlayerNicknameOrName",
            help = "Shows whether a player uses a real name or nickname. Note: case-sensitive.",
            handler = Whois
        };

        internal static void Whois(Player player, Command cmd) {
            string name = cmd.Next();
            if (name == null) {
                cdWhere.PrintUsage(player);
                return;
            }

            Player target = Server.FindPlayerByNick(name);
            if (target != null) {
                if (target.nick != target.name) {
                    player.Message("Player named " + target.name + " is using a nickname \"" + target.nick + "\"");
                } else {
                    player.Message("Player named " + target.name + " is not using any nickname.");
                }
            } else {
                player.NoPlayerMessage(name);
            }
        }

        static CommandDescriptor cdInfo = new CommandDescriptor {
            name = "info",
            aliases = new string[] { "pinfo" },
            consoleSafe = true,
            usage = "/info [PlayerName]",
            help = "Displays some information and stats about the player. " +
                   "If no name is given, shows your own stats.",
            handler = Info
        };

        internal static void Info(Player player, Command cmd) {
            string name = cmd.Next();
            if (name == null) {
                name = player.name;
            } else if (!player.Can(Permission.ViewOthersInfo)) {
                player.NoAccessMessage(Permission.ViewOthersInfo);
                return;
            }

            Player target = Server.FindPlayerByNick(name);
            if (target != null && target.nick != target.name) {
                player.Message(Color.Red + "Warning: Player named " + target.name + " is using a nickname \"" + target.nick + "\"");
                player.Message(Color.Red + "The information below is for the REAL " + name);
            }

            PlayerInfo info;
            if (!PlayerDB.FindPlayerInfo(name, out info)) {
                player.ManyPlayersMessage(name);
            } else if (info != null) {

                if (DateTime.Now.Subtract(info.lastLoginDate).TotalDays < 1) {
                    player.Message(String.Format("About {0}: Last login {1:F1} hours ago from {2}",
                                                        info.name,
                                                        DateTime.Now.Subtract(info.lastLoginDate).TotalHours,
                                                        info.lastIP));
                } else {
                    player.Message(String.Format("About {0}: Last login {1:F1} days ago from {2}",
                                                        info.name,
                                                        DateTime.Now.Subtract(info.lastLoginDate).TotalDays,
                                                        info.lastIP));
                }
                player.Message(String.Format("  Logged in {0} time(s) since {1:dd MMM yyyy}.",
                                                    info.timesVisited,
                                                    info.firstLoginDate));

                player.Message(String.Format("  Built {0} and deleted {1} blocks, and wrote {2} messages.",
                                                    info.blocksBuilt,
                                                    info.blocksDeleted,
                                                    info.linesWritten));

                if (player.info.classChangedBy != "-") {
                    player.Message(String.Format("  Promoted to {0} by {1} on {2:dd MMM yyyy}.",
                                                        info.playerClass.name,
                                                        info.classChangedBy,
                                                        info.classChangeDate));
                } else {
                    player.Message(String.Format("  Class is {0} (default).",
                                                        info.playerClass.name));
                }

                TimeSpan totalTime = info.totalTimeOnServer;
                if (Server.FindPlayerExact(player.name) != null) {
                    totalTime = totalTime.Add(DateTime.Now.Subtract(info.lastLoginDate));
                }
                player.Message(String.Format("  Spent a total of {0:F1} hours ({1:F1} minutes) here.",
                                                    totalTime.TotalHours,
                                                    totalTime.TotalMinutes));
            } else {
                player.NoPlayerMessage(name);
            }
        }

        static CommandDescriptor cdClassInfo = new CommandDescriptor {
            name = "cinfo",
            aliases = new string[] { "class", "classinfo" },
            consoleSafe = true,
            usage = "/cinfo ClassName",
            help = "Shows a list of permissions granted to a class. To see a list of all classes, use &H/classes",
            handler = ClassInfo
        };

        // Shows general information about a particular class.
        internal static void ClassInfo(Player player, Command cmd) {
            PlayerClass playerClass = ClassList.FindClass(cmd.Next());
            if (playerClass != null) {
                player.Message("Players of class \"" + playerClass.name + "\" can do the following:");
                string line = "";
                for (int i = 0; i < playerClass.permissions.Length; i++) {
                    if (playerClass.permissions[i]) {
                        string addition = Enum.GetName(typeof(Permission), (Permission)i).ToLower();
                        if (line.Length + addition.Length > 62) {
                            player.Message(line.Substring(0, line.Length - 2));
                            line = addition + ", ";
                        } else {
                            line += addition + ", ";
                        }
                    }
                }
                if (line.Length > 2) {
                    player.Message(line.Substring(0, line.Length - 2));
                }
            }
        }

        static CommandDescriptor cdClasses = new CommandDescriptor {
            name = "classes",
            consoleSafe = true,
            help = "Shows a list of all defined classes/ranks.",
            handler = Classes
        };

        internal static void Classes(Player player, Command cmd) {
            player.Message("Below is a list of classes. For detail see " + Color.Help + cdClassInfo.usage);
            foreach (PlayerClass classListEntry in ClassList.classesByIndex) {
                player.Message(classListEntry.color + "    " + classListEntry.name + " (rank " + classListEntry.rank + ")");
            }
        }

        static CommandDescriptor cdRules = new CommandDescriptor {
            name = "rules",
            consoleSafe = true,
            help = "Shows a list of rules defined by server operator(s).",
            handler = Rules
        };

        const string RulesFile = "rules.txt";

        // Prints rules (if any are defined)
        internal static void Rules(Player player, Command cmd) {
            if (!File.Exists(RulesFile)) {
                player.Message("Rules: Use common sense!");
            } else {
                try {
                    foreach (string ruleLine in File.ReadAllLines(RuleFile)) {
                        if (ruleLine.Trim().Length > 0) {
                            player.Message(Color.Announcement + ruleLine);
                        }
                    }
                } catch (Exception ex) {
                    Logger.Log("Error while trying to retrieve rules.txt: {0}", LogType.Error, ex.Message);
                    player.Message("Rules: Use common sense!");
                }
            }
        }
    }
}
